﻿#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "ngx_c_conf.h"
#include "ngx_func.h"
#include "ngx_global.h"
#include "ngx_macro.h"
#include "ngx_c_slogic.h"
#include "ngx_c_memory.h"
#include "ngx_c_crc32.h"

static void freeresource();

// 和设置标题有关的全局量
size_t  g_argvneedmem = 0;  // 保存下这些argv参数所需要的内存大小
size_t  g_envneedmem = 0;   // 环境变量所占内存大小
int     g_os_argc;          // 参数个数 
char    **g_os_argv;        // 原始命令行参数数组,在main中会被赋值
char    *gp_envmem = NULL;  // 指向自己分配的env环境变量的内存
int     g_daemonized = 0;
int     ngx_process;        // 进程类型
sig_atomic_t ngx_reap;      //标记子进程状态变化[一般是子进程发来SIGCHLD信号表示退出],sig_atomic_t:系统定义的类型：访问或改变这些变量需要在计算机的一条指令内完成
                                   //一般等价于int【通常情况下，int类型的变量通常是原子访问的，也可以认为 sig_atomic_t就是int类型的数据】

//和进程本身有关的全局量
pid_t ngx_pid;              // 当前进程的pid
pid_t ngx_parent;           // 父进程的pid
int     g_stopEvent;        // 标志程序退出,0不退出1，退出

// socket相关
CLogicSocket g_socket;      // socket全局对象
CThreadPool  g_threadpool;  // 线程池全局对象

int main(int argc, char* const* argv)
{         
    int exitcode = 0;           // 退出代码，先给0表示正常退出
    int i;                      // 临时用

    // 0. 先初始化的变量
    g_stopEvent = 0;            //标记程序是否退出，0不退出 

    ngx_pid = getpid();         // 取得进程pid
    ngx_parent = getppid();     // 取得父进程的id 

    // 1. 不需要释放内存的放在最上面
    g_argvneedmem = 0;          // 统计argv所占的内存
    ngx_reap = 0;               // 子进程状态变化标记为没有变化
    ngx_process = NGX_PROCESS_MASTER; // 当前进程标记为master进程
    for(i = 0; i < argc; i++)
    {
        g_argvneedmem += strlen(argv[i]) + 1; //+1是给\0留空间。
    } 
    // 统计环境变量所占的内存
    for(i = 0; environ[i]; i++) 
    {
        // +1是因为末尾有\0,是占实际内存位置的，要算进来
        g_envneedmem += strlen(environ[i]) + 1;
    } // end for

    g_os_argc = argc;           // 保存参数个数
    g_os_argv = (char **) argv; // 保存参数指针

    // 2. 初始化失败，就要直接退出的
    CConfig* pConfig = CConfig::GetInstance(); // 配置文件必须最先要，先把配置读出来，供后续使用 
    if (pConfig->Load("nginx.conf") == false)
    {
        ngx_log_stderr(0, "配置文件[%s]载入失败，退出!", "nginx.conf");
        goto lblexit;
    }
    ngx_log_init();                            // 日志初始化(创建/打开日志文件)

    // 2.1 内存单例初始化，返回值不用保存
    CMemory::GetInstance();
    // 2.2 crc32校验算法单例初始化，返回值不用保存
    CCRC32::GetInstance();

    // 3. 一些初始化函数    
    if(ngx_init_signals() != 0)         // 信号初始化
    {
        exitcode = 1;
        goto lblexit;
    }
    if (g_socket.Initialize() == false) // 监听套接字初始化
    {
        exitcode = 1;
        goto lblexit;
    }

    // 4. 一些不好归类的其他类别的代码
    ngx_init_setproctitle();           // 把环境变量搬家

    // 5. 创建守护进程
    if (pConfig->GetIntDefault("Daemon", 0) == 1)
    {// 配置了作为守护进程启动
        int ret = ngx_daemon();
        if (-1 == ret)
        {// 创建守护进程失败
            exitcode = -1;
            goto lblexit;
        }
        else if (1 == ret)
        {// 父进程就直接退出了
            freeresource();
            exitcode = 0;
            return exitcode;
        }
        // 子进程现在是一个守护进程，脱离了终端(作为master进程)
        g_daemonized = 1;
    }

    // 6. 开始正式的主工作流程，主流程一致在下边这个函数里循环
    ngx_master_process_cycle();
lblexit:
    // 释放所有的资源
    ngx_log_stderr(0, "master process exit!!!");
    freeresource();
    return exitcode;
}

// 释放资源
void freeresource()
{
    // 对于因为设置可执行程序标题导致的环境变量分配的内存，我们应该释放
    if(gp_envmem)
    {
        delete []gp_envmem;
        gp_envmem = NULL;
    }

    // 关闭日志文件
    if(ngx_log.fd != STDERR_FILENO && ngx_log.fd != -1)  
    {        
        close(ngx_log.fd); // 不用判断结果了
        ngx_log.fd = -1; // 标记下，防止被再次close        
    }
}
